#include "A_Config.h"
class AppBilibili : public AppBase
{
private:
    /* data */
public:
    AppBilibili()
    {
        name = "bilibili";
        title = "Bilibili";
        description = "自制Bilibili客户端（正在开发中）";
        image = "\xEF\x81\x8B";
    }
    void setup();
    void loop();
    void destruct();
};
#define BILIBILI_FANS_COUNT_POSITION -60
#define QRCODE_SIZE 160
String UID;
DynamicJsonDocument doc(4096);
String Response;
String Cookies;
String Headers;
int HeaderCount;
lv_color_t *cbuf;
WiFiClient client;
HTTPClient http;
JsonObject obj;
int fans_before = 0;
///////////////////////////////////////////////////////

static lv_obj_t *lbl_fans_count[2];
static lv_obj_t *lbl_reply_count;
extern void wf_clock_anim_set(lv_obj_t *label[2], uint16_t y, uint16_t delaytime);
///////////////////////////////////////////////////////
// 从cookie中获取UID
static void updateUID()
{
    uint16_t i = Cookies.indexOf("DedeUserID=") + 11;
    if (i == -1)
    {
        UID = "";
        return;
    }
    UID = Cookies.substring(i, Cookies.indexOf(';', i));
}

static String HTTPGET(const char *url)
{
    HTTPClient http;

    String payload = "";
    for (uint8_t i = 0; i < 10; ++i)
    {
        if (WiFi.status() != WL_CONNECTED)
            return "";
        if (http.begin(url))
        {
            http.addHeader("Content-Type", "application/x-www-form-urlencoded");
            http.addHeader("Referer", "http://www.bilibili.com");
            if (Cookies != "")
                http.addHeader("Cookie", Cookies);
            if (http.GET() == HTTP_CODE_OK)
            {
                payload = http.getString();
                http.end();
                return payload;
            }
        }
        vTaskDelay(100);
    }
    return payload;
}

static bool checkCookies()
{
    String testURL = "http://api.bilibili.com/x/relation/followers?vmid=" + UID + "&ps=1&pn=6";
    if (http.begin(client, testURL))
    {
        http.addHeader("Content-Type", "application/x-www-form-urlencoded");
        http.addHeader("Referer", "http://www.bilibili.com");
        http.addHeader("Cookie", Cookies);
        if (http.GET() == HTTP_CODE_OK)
        {
            Response = http.getString();
        }
        http.end();
    }
    deserializeJson(doc, Response);
    obj = doc.as<JsonObject>();
    if (obj["code"] == 0)
    {
        return true;
    }
    return false;
}

static bool login(lv_obj_t *scr)
{
    String URL = "";
    String oauthKey;
    String Response;
    uint32_t millisStart;
    lv_obj_t *msg;
    hal.DoNotSleep = true;
    while (1)
    {
    RebuildQRCode:
        millisStart = millis();
        msg = full_screen_msgbox_create(BIG_SYMBOL_INFO, "提示", "正在获取二维码");
        Response = HTTPGET("http://passport.bilibili.com/qrcode/getLoginUrl");
        if (Response == "")
        {
            full_screen_msgbox_del(msg);
            full_screen_msgbox(BIG_SYMBOL_CROSS, "错误", "API很可能已失效，请联系开发者", FULL_SCREEN_BG_CROSS);
            hal.DoNotSleep = false;
            return false;
        }

        deserializeJson(doc, Response);
        obj = doc.as<JsonObject>();

        URL = obj["data"]["url"].as<String>();
        oauthKey = obj["data"]["oauthKey"].as<String>();
        full_screen_msgbox_del(msg);
        lv_obj_t *qr = lv_qrcode_create(scr, QRCODE_SIZE, lv_color_black(), lv_color_white());
        lv_obj_center(qr);
        lv_qrcode_update(qr, URL.c_str(), URL.length());

        while (millis() - millisStart < 180000)
        {
            if (hal.axpShortPress)
            {
                hal.axpShortPress = false;
                goto err;
            }
            vTaskDelay(1000);
            if (http.begin(client, "http://passport.bilibili.com/qrcode/getLoginInfo"))
            {
                http.addHeader("Content-Type", "application/x-www-form-urlencoded");
                http.addHeader("Referer", "http://www.bilibili.com");
                http.addHeader("User-Agent", "curl/7.70.0");
                if (http.POST("oauthKey=" + oauthKey) == HTTP_CODE_OK)
                {
                    Response = http.getString();
                }
            }

            deserializeJson(doc, Response);
            obj = doc.as<JsonObject>();

            if (obj["status"] == true)
            {
                Response = obj["data"]["url"].as<String>();
                Cookies = Response.substring(Response.indexOf("?") + 1);
                Cookies.replace("&", ";");
                http.end();
                lv_qrcode_delete(qr);
                goto scaned;
            }
            else
            {
                if (obj["data"] == -1 || obj["data"] == -2)
                {
                    full_screen_msgbox(BIG_SYMBOL_CROSS, "超时", "二维码超时，准备重新生成", FULL_SCREEN_BG_CROSS, 1000);
                    http.end();
                    lv_qrcode_delete(qr);
                    goto RebuildQRCode;
                }
                else if (obj["data"] == -5)
                {
                }
            }
            http.end();
        }
        continue;
    err:
        break;
    }
scaned:
    updateUID();
    if (checkCookies())
    {
        msg = full_screen_msgbox_create(BIG_SYMBOL_CHECK, "成功", "二维码登录成功", FULL_SCREEN_BG_CHECK);
        settings.setString("cookies", Cookies);
        settings.writeSettings(CONFIG_FILE_NAME);
        full_screen_msgbox_wait_del(msg, 1000);
        hal.DoNotSleep = false;
        return true;
    }
    else
    {
        full_screen_msgbox(BIG_SYMBOL_CROSS, "错误", "登录失败");
        hal.DoNotSleep = false;
        return false;
    }
}

static void updateInfo()
{
    uint16_t like;
    uint16_t reply;
    uint16_t msg;
    Response = HTTPGET("https://api.bilibili.com/x/msgfeed/unread");
    deserializeJson(doc, Response);
    obj = doc.as<JsonObject>();
    if (obj["code"] != 0)
    {
        // 获取消息数失败
        Serial.println(Response);
        return;
    }
    like = obj["data"]["like"];
    reply = obj["data"]["reply"];
    Response = HTTPGET("https://api.vc.bilibili.com/session_svr/v1/session_svr/single_unread");
    deserializeJson(doc, Response);
    obj = doc.as<JsonObject>();
    if (obj["code"] != 0)
    {
        // 获取私信数失败
        Serial.println(Response);
        return;
    }
    msg = obj["data"]["unfollow_unread"].as<int>() + obj["data"]["follow_unread"].as<int>();
    lv_label_set_text_fmt(lbl_reply_count, "点赞：%d\n回复：%d\n私信：%d", like, reply, msg);
    // 关注数
    Response = HTTPGET(("https://api.bilibili.com/x/relation/stat?jsonp=jsonp&vmid=" + UID).c_str());
    deserializeJson(doc, Response);
    obj = doc.as<JsonObject>();
    if (obj["code"] != 0)
    {
        // 获取关注数失败
        Serial.println(Response);
        return;
    }
    int fans_now = obj["data"]["follower"];
    if (fans_now != fans_before)
    {
        fans_before = fans_now;
        LOCKLV();
        lv_label_set_text_fmt(lbl_fans_count[1], "%d", fans_now);
        wf_clock_anim_set(lbl_fans_count, BILIBILI_FANS_COUNT_POSITION, 0);
        UNLOCKLV();
    }
}

void AppBilibili::setup()
{
    lbl_fans_count[0] = lv_label_create(scr);
    lv_label_set_text(lbl_fans_count[0], "0");
    lv_obj_set_style_text_font(lbl_fans_count[0], &num_32px, 0);
    lv_obj_align(lbl_fans_count[0], LV_ALIGN_CENTER, 0, BILIBILI_FANS_COUNT_POSITION);

    lbl_fans_count[1] = lv_label_create(scr);
    lv_label_set_text(lbl_fans_count[1], "");
    lv_obj_set_style_text_font(lbl_fans_count[1], &num_32px, 0);
    lv_obj_align(lbl_fans_count[1], LV_ALIGN_CENTER, 0, BILIBILI_FANS_COUNT_POSITION);

    lbl_reply_count = label(scr, "???", 0, 0, true, 200);
    lv_obj_align(lbl_reply_count, LV_ALIGN_CENTER, 0, 0);

    Cookies = settings.getString("cookies");
    WiFiMgr.autoConnect();
    updateUID();
    if (checkCookies() == false)
    {
        if (login(scr) == false)
        {
            full_screen_msgbox(BIG_SYMBOL_CROSS, "登录",
                               "登录失败，正在退出", FULL_SCREEN_BG_CROSS, 2000);
            hal.DoNotSleep = false;
            appManager.goBack();
            return;
        }
    }
}

void AppBilibili::loop()
{
    static int i = 1000;
    AppBase::loop();
    i++;
    if (i > 100)
    {
        i = 0;
        updateInfo();
    }
}

void AppBilibili::destruct()
{
}

static AppBilibili app;
